﻿/******************************************************************************
 * 
 * Announce: CSharpKit, Basic algorithms, components and definitions.
 *           Copyright (C) ShenYongchen.
 *           All rights reserved.
 *   Author: 申永辰.郑州 (shenyczz@163.com)
 *  WebSite: http://github.com/shenyczz/CSharpKit
 *
 * THIS CODE IS LICENSED UNDER THE MIT LICENSE (MIT).
 * THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF 
 * ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
 * IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
 * PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
 * 
******************************************************************************/
using System;
using CSharpKit.Threading.Tasks;

namespace CSharpKit.Numerics.Theories
{
    /// <summary>
    /// 窗口函数（Independent） - WFunctions
    /// </summary>
    public static class Window
    {
        /// <summary>
        /// Hamming window（哈明窗口）.
        /// Named after Richard Hamming.
        /// Symmetric version, useful e.g. for filter design purposes.
        /// </summary>
        public static double[] Hamming(int width)
        {
            const double a = 0.53836;
            const double b = -0.46164;

            double phaseStep = (2.0 * System.Math.PI) / (width - 1.0);

            var w = new double[width];
            for (int i = 0; i < w.Length; i++)
            {
                w[i] = a + b * System.Math.Cos(i * phaseStep);
            }
            return w;
        }

        /// <summary>
        /// Hamming window. Named after Richard Hamming.
        /// Periodic version, useful e.g. for FFT purposes.
        /// </summary>
        public static double[] HammingPeriodic(int width)
        {
            const double a = 0.53836;
            const double b = -0.46164;

            double phaseStep = (2.0 * System.Math.PI) / width;

            var w = new double[width];
            for (int i = 0; i < w.Length; i++)
            {
                w[i] = a + b * System.Math.Cos(i * phaseStep);
            }
            return w;
        }

        /// <summary>
        /// Hann（汉宁） window. Named after Julius von Hann.
        /// Symmetric version, useful e.g. for filter design purposes.
        /// </summary>
        public static double[] Hann(int width)
        {
            double phaseStep = (2.0 * System.Math.PI) / (width - 1.0);

            var w = new double[width];
            for (int i = 0; i < w.Length; i++)
            {
                w[i] = 0.5 - 0.5 * System.Math.Cos(i * phaseStep);
            }
            return w;
        }

        /// <summary>
        /// Hann window. Named after Julius von Hann.
        /// Periodic version, useful e.g. for FFT purposes.
        /// </summary>
        public static double[] HannPeriodic(int width)
        {
            double phaseStep = (2.0 * System.Math.PI) / width;

            var w = new double[width];
            for (int i = 0; i < w.Length; i++)
            {
                w[i] = 0.5 - 0.5 * System.Math.Cos(i * phaseStep);
            }
            return w;
        }

        /// <summary>
        /// Cosine window(余弦窗).
        /// Symmetric version, useful e.g. for filter design purposes.
        /// </summary>
        public static double[] Cosine(int width)
        {
            double phaseStep = System.Math.PI / (width - 1.0);

            var w = new double[width];
            for (int i = 0; i < w.Length; i++)
            {
                w[i] = System.Math.Sin(i * phaseStep);
            }
            return w;
        }

        /// <summary>
        /// Cosine window.
        /// Periodic version, useful e.g. for FFT purposes.
        /// </summary>
        public static double[] CosinePeriodic(int width)
        {
            double phaseStep = System.Math.PI / width;

            var w = new double[width];
            for (int i = 0; i < w.Length; i++)
            {
                w[i] = System.Math.Sin(i * phaseStep);
            }
            return w;
        }

        /// <summary>
        /// Lanczos window.
        /// Symmetric version, useful e.g. for filter design purposes.
        /// </summary>
        public static double[] Lanczos(int width)
        {
            double phaseStep = 2.0 / (width - 1.0);

            var w = new double[width];
            for (int i = 0; i < w.Length; i++)
            {
                w[i] = Trigon.Sinc(i * phaseStep - 1.0);
            }
            return w;
        }

        /// <summary>
        /// Lanczos window.
        /// Periodic version, useful e.g. for FFT purposes.
        /// </summary>
        public static double[] LanczosPeriodic(int width)
        {
            double phaseStep = 2.0 / width;

            var w = new double[width];
            for (int i = 0; i < w.Length; i++)
            {
                w[i] = Trigon.Sinc(i * phaseStep - 1.0);
            }
            return w;
        }

        /// <summary>
        /// Gauss window.
        /// </summary>
        public static double[] Gauss(int width, double sigma)
        {
            double a = (width - 1) / 2.0;

            var w = new double[width];
            for (int i = 0; i < w.Length; i++)
            {
                double exponent = (i - a) / (sigma * a);
                w[i] = System.Math.Exp(-0.5 * exponent * exponent);
            }
            return w;
        }

        /// <summary>
        /// Blackman window(布莱克曼窗).
        /// </summary>
        public static double[] Blackman(int width)
        {
            const double alpha = 0.16;
            const double a = 0.5 - 0.5 * alpha;
            const double b = 0.5 * alpha;

            int last = width - 1;
            double c = 2.0 * System.Math.PI / last;
            double d = 2.0 * c;

            var w = new double[width];
            for (int i = 0; i < w.Length; i++)
            {
                w[i] = a
                       - 0.5 * System.Math.Cos(i * c)
                       + b * System.Math.Cos(i * d);
            }
            return w;
        }

        /// <summary>
        /// Blackman-Harris window.
        /// </summary>
        public static double[] BlackmanHarris(int width)
        {
            const double a = 0.35875;
            const double b = -0.48829;
            const double c = 0.14128;
            const double d = -0.01168;

            int last = width - 1;
            double e = 2.0 * System.Math.PI / last;
            double f = 2.0 * e;
            double g = 3.0 * e;

            var w = new double[width];
            for (int i = 0; i < w.Length; i++)
            {
                w[i] = a
                       + b * System.Math.Cos(e * i)
                       + c * System.Math.Cos(f * i)
                       + d * System.Math.Cos(g * i);
            }
            return w;
        }

        /// <summary>
        /// Blackman-Nuttall window.
        /// </summary>
        public static double[] BlackmanNuttall(int width)
        {
            const double a = 0.3635819;
            const double b = -0.4891775;
            const double c = 0.1365995;
            const double d = -0.0106411;

            int last = width - 1;
            double e = 2.0 * System.Math.PI / last;
            double f = 2.0 * e;
            double g = 3.0 * e;

            var w = new double[width];
            for (int i = 0; i < w.Length; i++)
            {
                w[i] = a
                       + b * System.Math.Cos(e * i)
                       + c * System.Math.Cos(f * i)
                       + d * System.Math.Cos(g * i);
            }
            return w;
        }

        /// <summary>
        /// Bartlett window.
        /// </summary>
        public static double[] Bartlett(int width)
        {
            int last = width - 1;
            double a = 2.0 / last;
            double b = last / 2.0;

            var w = new double[width];
            for (int i = 0; i < w.Length; i++)
            {
                w[i] = a * (b - System.Math.Abs(i - b));
            }
            return w;
        }

        /// <summary>
        /// Bartlett-Hann window.
        /// </summary>
        public static double[] BartlettHann(int width)
        {
            const double a = 0.62;
            const double b = -0.48;
            const double c = -0.38;

            int last = width - 1;
            double d = 1.0 / last;
            double e = 2.0 * System.Math.PI / last;

            var w = new double[width];
            for (int i = 0; i < w.Length; i++)
            {
                w[i] = a
                       + b * System.Math.Abs(i * d - 0.5)
                       + c * System.Math.Cos(i * e);
            }
            return w;
        }

        /// <summary>
        /// Nuttall window.
        /// </summary>
        public static double[] Nuttall(int width)
        {
            const double a = 0.355768;
            const double b = -0.487396;
            const double c = 0.144232;
            const double d = -0.012604;

            int last = width - 1;
            double e = 2.0 * System.Math.PI / last;
            double f = 2.0 * e;
            double g = 3.0 * e;

            var w = new double[width];
            for (int i = 0; i < w.Length; i++)
            {
                w[i] = a
                       + b * System.Math.Cos(e * i)
                       + c * System.Math.Cos(f * i)
                       + d * System.Math.Cos(g * i);
            }
            return w;
        }

        /// <summary>
        /// Flat top window.
        /// </summary>
        public static double[] FlatTop(int width)
        {
            const double a = 1.0;
            const double b = -1.93;
            const double c = 1.29;
            const double d = -0.388;
            const double e = 0.032;

            int last = width - 1;
            double f = 2.0 * System.Math.PI / last;
            double g = 2.0 * f;
            double h = 3.0 * f;
            double k = 4.0 * f;

            double[] w = new double[width];
            for (int i = 0; i < w.Length; i++)
            {
                w[i] = a
                       + b * System.Math.Cos(f * i)
                       + c * System.Math.Cos(g * i)
                       + d * System.Math.Cos(h * i)
                       + e * System.Math.Cos(k * i);
            }
            return w;
        }

        /// <summary>
        /// Uniform rectangular (Dirichlet) window.
        /// </summary>
        public static double[] Dirichlet(int width)
        {
            var w = new double[width];
            for (int i = 0; i < w.Length; i++)
            {
                w[i] = 1.0;
            }
            return w;
        }

        /// <summary>
        /// Triangular window.
        /// </summary>
        public static double[] Triangular(int width)
        {
            double a = 2.0 / width;
            double b = width / 2.0;
            double c = (width - 1) / 2.0;

            var w = new double[width];
            for (int i = 0; i < w.Length; i++)
            {
                w[i] = a * (b - System.Math.Abs(i - c));
            }
            return w;
        }

        /// <summary>
        /// Tukey tapering window. A rectangular window bounded
        /// by half a cosine window on each side.
        /// </summary>
        /// <param name="width">Width of the window</param>
        /// <param name="r">Fraction of the window occupied by the cosine parts</param>
        public static double[] Tukey(int width, double r = 0.5)
        {

            if (r <= 0)
            {
                return Repeat(width, 1.0);
            }
            else if (r >= 1)
            {
                return Hann(width);
            }

            var w = new double[width];
            var period = (width - 1) * r;
            var step = 2 * System.Math.PI / period;
            var b1 = (int)System.Math.Floor((width - 1) * r * 0.5 + 1);
            var b2 = width - b1;
            for (var i = 0; i < b1; i++)
            {
                w[i] = (1 - System.Math.Cos(i * step)) * 0.5;
            }
            for (var i = b1; i < b2; i++)
            {
                w[i] = 1;
            }
            for (var i = b2; i < width; i++)
            {
                w[i] = w[width - i - 1];
            }
            return w;
        }


        // 重复值
        private static T[] Repeat<T>(int length, T value)
        {
            if (length < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(length));
            }

            var data = new T[length];

            for (int i = 0; i < length; i++)
            {
                data[i] = value;
            }

            //CommonParallel.For(0, data.Length, 4096, (a, b) =>
            //{
            //    for (int i = a; i < b; i++)
            //    {
            //        data[i] = value;
            //    }
            //});

            return data;
        }



        //}}@@@
    }



}
